<?php

namespace yifang\route\dispatch;

use yifang\route\Dispatch;
use yifang\App;
use Exception;
use ReflectionMethod;
use ReflectionException;
use yifang\exception\ClassNotFoundException;
use yifang\exception\HttpException;

class Controller extends Dispatch
{
    /**
     * 控制器名
     * @var string
     */
    protected $controller;

    /**
     * 操作名
     * @var string
     */
    protected $actionName;

    public function init(App $app)
    {
        parent::init($app);
        $result = $this->dispatch;
        if (is_string($result)) {
            $result = explode('/', $result);
        }
        // 获取控制器名
        $this->controller = strip_tags($result[0] ?: $this->rule->config('default_controller'));
        // 获取操作名
        $this->actionName = strip_tags($result[1] ?: $this->rule->config('default_action'));
        // 设置当前请求的控制器、操作
        $this->request->setController($this->controller)->setAction($this->actionName);
        $class = $this->getClass($this->controller);
        $this->request->setControllerFull($class);
    }

    public function exec()
    {
        try {
            // 实例化控制器
            $instance = $this->controller($this->controller);
        } catch (ClassNotFoundException $e) {
            throw new HttpException(404, 'controller not exists:' . $e->getClass());
        }

        return $this->app->middleware->pipeline('controller')
            ->send($this->request)
            ->then(function () use ($instance) {
                // 获取当前操作名
                $suffix = $this->rule->config('action_suffix');
                $action = $this->actionName . $suffix;

                if (is_callable([$instance, $action])) {
                    $vars = $this->request->param();
                    try {
                        $reflect = new ReflectionMethod($instance, $action);
                        // 严格获取当前操作方法名
                        $actionName = $reflect->getName();
                        if ($suffix) {
                            $actionName = substr($actionName, 0, -strlen($suffix));
                        }
                        $this->request->setAction($actionName);
                    } catch (ReflectionException $e) {
                        $reflect = new ReflectionMethod($instance, '__call');
                        $vars    = [$action, $vars];
                        $this->request->setAction($action);
                    }
                } else {
                    // 操作不存在
                    throw new HttpException(404,'method not exists:' . get_class($instance) . '->' . $action . '()');
                }

                $data = $this->app->invokeReflectMethod($instance, $reflect, $vars);
                return $this->autoResponse($data);
            });
    }

    /**
     * 获取完整的控制器
     */
    public function getClass($name)
    {
        $suffix = $this->rule->config('controller_suffix') ? 'Controller' : '';
        $controllerLayer = $this->rule->config('controller_layer') ?: 'controller';
        $emptyController = $this->rule->config('empty_controller') ?: 'Error';
        $class = $this->app->parseClass($controllerLayer, $name . $suffix,$this->namespace);
        if(isset($this->param["_api_version_"]) && $this->param["_api_version_"]){
            $class=$this->autoVersion($this->param["_api_version_"],$class,$name);
        }
        if (class_exists($class)) {
            return $class;
        } elseif ($emptyController && class_exists($emptyClass = $this->app->parseClass($controllerLayer, $emptyController . $suffix))) {
            return $emptyClass;
        }
        return $class;
    }

    /**
     * 实例化访问控制器
     * @access public
     * @param string $name 资源地址
     * @return object
     */
    public function controller($name)
    {
        $class = $this->getClass($name);
        if (class_exists($class)) {
            return $this->app->make($class, [], true);
        }
        throw new ClassNotFoundException('class not exists:' . $class, $class);
    }

    /**
     * 自动路由版本
     */
    public function autoVersion($version,$class,$name){
        $controllerPath=getControllerPath($name);
        $suffix=strstr($class,$controllerPath);
        $prefix=strstr($class,$controllerPath,true);

        $action_suffix = $this->rule->config('action_suffix');
        $action = $this->actionName . $action_suffix;

        $controller=$class;
        for ($i = $version; $i > 0; $i--) {
            $controller=$prefix."v".$i."\\".$suffix;
            if(method_exists($controller,$action)){
                break;
            }
        }
        return $controller;
    }
}